package coc.core.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.safety.Whitelist;
import org.jsoup.select.Elements;

import coo.base.constants.Chars;

/**
 * 对Jsoup的Elements对象封装扩展类。
 */
public class JsoupElements extends Elements {
	/**
	 * 构造方法。
	 */
	public JsoupElements() {
		super();
	}

	/**
	 * 构造方法。
	 * 
	 * @param element
	 *            Jsoup元素
	 */
	public JsoupElements(Element element) {
		super(element);
	}

	/**
	 * 构造方法。
	 * 
	 * @param elements
	 *            Jsoup元素集合
	 */
	public JsoupElements(Collection<Element> elements) {
		super(elements);
	}

	/**
	 * 构造方法。
	 * 
	 * @param elements
	 *            Jsoup元素列表
	 */
	public JsoupElements(List<Element> elements) {
		super(elements);
	}

	/**
	 * 构造方法。
	 * 
	 * @param html
	 *            HTML代码
	 */
	public JsoupElements(String html) {
		this(Jsoup.parse(html));
	}

	/**
	 * 构造方法。
	 * 
	 * @param html
	 *            HTML代码
	 * @param baseUri
	 *            基本URI
	 */
	public JsoupElements(String html, String baseUri) {
		this(Jsoup.parse(html, baseUri));
	}

	/**
	 * 清理HTML只保留基础标签。
	 * 
	 * @see org.jsoup.safety.Whitelist#basic()
	 * 
	 * @return 返回清理后的HTML。
	 */
	public String basic() {
		return clean(Whitelist.basic());
	}

	/**
	 * 清理HTML只保留基础标签和图片标签。
	 * 
	 * @see org.jsoup.safety.Whitelist#basicWithImages()
	 * 
	 * @return 返回清理后的HTML。
	 */
	public String basicWithImages() {
		return clean(Whitelist.basicWithImages());
	}

	/**
	 * 清理HTML去除所有标签。
	 * 
	 * @see org.jsoup.safety.Whitelist#none()
	 * 
	 * @return 返回清理后的HTML。
	 */
	public String none() {
		return clean(Whitelist.none());
	}

	/**
	 * 清理HTML保留简单文本标签。
	 * 
	 * @see org.jsoup.safety.Whitelist#simpleText()
	 * 
	 * @return 返回清理后的HTML。
	 */
	public String simpleText() {
		return clean(Whitelist.simpleText());
	}

	/**
	 * 清理HTML保留常规标签。
	 * 
	 * @see org.jsoup.safety.Whitelist#relaxed()
	 * 
	 * @return 返回清理后的HTML。
	 */
	public String relaxed() {
		return clean(Whitelist.relaxed());
	}

	/**
	 * 清理HTML。
	 * 
	 * @param whitelist
	 *            清理规则
	 * @return 返回清理后的HTML。
	 */
	public String clean(Whitelist whitelist) {
		if (!isEmpty()) {
			return Jsoup.clean(html(), first().baseUri(), whitelist);
		} else {
			return "";
		}
	}

	/**
	 * 移除包含的元素及所有子元素的指定属性。
	 * 
	 * @param attrs
	 *            待移除的属性名称，可以指定多个，用“,”分隔
	 * @return 返回当前对象。
	 */
	public JsoupElements removeAttrs(String attrs) {
		for (Element element : this) {
			for (String attr : attrs.split(Chars.COMMA)) {
				element.getAllElements().removeAttr(attr);
			}
		}
		return this;
	}

	/**
	 * 移除包含的元素及所有子元素的指定标签。
	 * 
	 * @param tags
	 *            待删除的标签名，可以指定多个，用“,”分隔
	 * @return 返回当前对象。
	 */
	public JsoupElements removeTags(String tags) {
		for (Element element : this) {
			for (String tag : tags.split(Chars.COMMA)) {
				element.select(tag).remove();
			}
		}
		return this;
	}

	/**
	 * 覆盖父类以支持（eq/lt/gt）语法。
	 * 
	 * @param cssQuery
	 *            CSS查询表达式
	 * @return 返回查询到的元素列表。
	 */
	@Override
	public JsoupElements select(String cssQuery) {
		Integer minOpIndex = getMinOpIndex(cssQuery);
		if (minOpIndex != -1) {
			// 处理多条件CSS查询表达式
			if (cssQuery.contains(",")) {
				return select(cssQuery.split(","));
			}
			// 处理单条件CSS查询表达式
			String ps = cssQuery.substring(0, minOpIndex);
			String ns = cssQuery.substring(minOpIndex);
			Integer cursor = ns.indexOf(")") + 1;
			String op = ns.substring(0, cursor);
			ns = ns.substring(cursor);

			JsoupElements elements = select(ps, op);
			if (StringUtils.isNotEmpty(ns)) {
				elements = elements.select(ns);
			}
			return elements;
		} else {
			return new JsoupElements(super.select(cssQuery));
		}
	}

	/**
	 * 获取符合多个CSS查询表达式及操作符的子元素集合。
	 * 
	 * @param cssQuerys
	 *            多个CSS查询表达式
	 * @return 返回符合多个CSS查询表达式及操作符的子元素集合。
	 */
	private JsoupElements select(String[] cssQuerys) {
		JsoupElements elements = new JsoupElements();
		for (String cssQuery : cssQuerys) {
			elements.addAll(select(cssQuery));
		}
		return elements;
	}

	/**
	 * 获取符合CSS查询表达式及操作符的子元素集合。
	 * 
	 * @param cssQuery
	 *            CSS查询表达式
	 * @param operator
	 *            操作符(eq/lt/gt)
	 * @return 返回符合CSS查询表达式及操作符的子元素集合。
	 */
	private JsoupElements select(String cssQuery, String operator) {
		Integer cursor = Integer.valueOf(operator.replaceAll("\\D", ""));
		JsoupElements elements = select(cssQuery);
		if (!elements.isEmpty()) {
			if (operator.startsWith(":eq")) {
				elements = new JsoupElements(elements.eq(cursor));
			}
			if (operator.startsWith(":lt")) {
				elements = new JsoupElements(elements.subList(0, cursor));
			}
			if (operator.startsWith(":gt")) {
				// 当表达式选中元素个数大于游标数时才执行，否则会抛出异常。
				if (elements.size() > cursor) {
					elements = new JsoupElements(elements.subList(cursor + 1,
							elements.size()));
				}
			}
		}
		return elements;
	}

	/**
	 * 获取CSS查询表达式中第一个的eq/lt/gt函数的开始位置。
	 * 
	 * @param cssQuery
	 *            CSS查询表达式
	 * @return 返回表达式中第一个的eq/lt/gt函数的开始位置。
	 */
	private Integer getMinOpIndex(String cssQuery) {
		Integer minOpIndex = -1;
		List<Integer> indexs = new ArrayList<Integer>();
		Integer eqIndex = cssQuery.indexOf(":eq");
		if (eqIndex != -1) {
			indexs.add(eqIndex);
		}
		Integer ltIndex = cssQuery.indexOf(":lt");
		if (ltIndex != -1) {
			indexs.add(ltIndex);
		}
		Integer gtIndex = cssQuery.indexOf(":gt");
		if (gtIndex != -1) {
			indexs.add(gtIndex);
		}
		if (!indexs.isEmpty()) {
			Collections.sort(indexs);
			minOpIndex = indexs.get(0);
		}
		return minOpIndex;
	}
}
